#include <QApplication>
#include <QWidget>
#include <QMainWindow>
#include <QGridLayout>
#include <QFrame>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QRadioButton>
	
#include <odv/odvCore/ViewerCameraSync.h>
#include <odv/odvLog/LogStream.h>
#include <odv/odvGUI/ViewerWidget.h>
#include <odv/odvGUI/QtGuiManager.h>
#include <odv/odvUtils/StringUtils.h>

#include <string>
#include <map>
#include <stack>

std::string addColorMap(std::shared_ptr<odv::Viewer> viewer);
std::string addImage(std::shared_ptr<odv::Viewer> viewer);
std::string addFeature(std::shared_ptr<odv::Viewer> viewer);
std::string addContourLine(std::shared_ptr<odv::Viewer> viewer);
std::string addPointIcon(std::shared_ptr<odv::Viewer> viewer);

std::shared_ptr<odv::Viewer> g_currentViewer;

struct ViewerData
{
	std::map<QWidget*, std::string> widgetLayerIdMap;
};

std::shared_ptr<odv::QtGuiManager> g_guiManager;
std::map<std::shared_ptr<odv::Viewer>, ViewerData> g_viewerData;

int main(int argc, char** argv)
{
	QApplication app(argc, argv);

	odv::Logger::init(argv[0]);

	QMainWindow window;
	window.setCentralWidget(new QWidget());
	auto layout = new QGridLayout;
	window.centralWidget()->setLayout(layout);
	window.resize(1920, 1080);

	auto viewerWidget1 = std::make_shared<odv::ViewerWidget>(odv::CoordinateSystem::Projected, odv::MapProjection::EquidistantCylindrical);
	auto viewerWidget2 = std::make_shared<odv::ViewerWidget>(odv::CoordinateSystem::Geographic);
	auto viewerWidget3 = std::make_shared<odv::ViewerWidget>(odv::CoordinateSystem::Geographic);
	auto viewerWidget4 = std::make_shared<odv::ViewerWidget>(odv::CoordinateSystem::Projected, odv::MapProjection::Mercator);

	std::vector<std::shared_ptr<odv::ViewerWidget>> widgets
	{
		viewerWidget1,
		viewerWidget2,
		viewerWidget3,
		viewerWidget4,
	};

	for (auto w : widgets)
	{
		auto guiManager = std::make_shared<odv::QtGuiManager>();
		guiManager->attachWidget(w->getQtWidget());
		w->getViewer()->setGuiManager(guiManager);
	}

	layout->addWidget(viewerWidget1->getQtWidget(), 0, 0);
	layout->addWidget(viewerWidget2->getQtWidget(), 0, 1);
	layout->addWidget(viewerWidget3->getQtWidget(), 1, 0);
	layout->addWidget(viewerWidget4->getQtWidget(), 1, 1);

	window.show();

	auto viewer1 = viewerWidget1->getViewer();
	auto viewer2 = viewerWidget2->getViewer();
	auto viewer3 = viewerWidget3->getViewer();
	auto viewer4 = viewerWidget4->getViewer();

	std::vector<std::shared_ptr<odv::Viewer>> viewers =
	{
		viewer1,
		viewer2,
		viewer3,
		viewer4,
	};

	for (int i = 0; i < viewers.size(); ++i)
	{
		viewers[i]->setName(odv::StringUtils::format("viewer-{}", i + 1));
	}

	g_currentViewer = viewer1;

	std::map<std::shared_ptr<odv::Viewer>, std::string> viewerImageIds;
	std::map<std::shared_ptr<odv::Viewer>, std::string> viewerFeatureIds;

	for (auto v : viewers)
	{
		g_viewerData.emplace(v, ViewerData());

		v->addMouseButtonHandler("vm", [](odv::Viewer* viewer, odv::EventType eventType, odv::MouseButton button, int x, int y)
		{
			if (eventType == odv::EventType::RELEASE)
			{
				ODVLOG_INFO(odv::StringUtils::format("{}: button {}", viewer->getName(), (int)button));
				g_currentViewer = viewer->shared_from_this();
			}
		});
	}

	auto viewerSync = std::make_shared<odv::ViewerCameraSync>();

	//功能面板
	{
		auto frame = new QFrame(&window);
		frame->setStyleSheet("color: rgb(170, 0, 255);");
		frame->setGeometry(10, 100, 200, 600);
		auto frameLayout = new QVBoxLayout;
		frameLayout->setAlignment(Qt::AlignTop);
		frame->setLayout(frameLayout);
		frame->show();

		//分屏
		{
			auto groupBox = new QGroupBox("分屏");
			groupBox->setLayout(new QVBoxLayout);
			frameLayout->addWidget(groupBox);

			{
				auto button = new QRadioButton("普通");
				groupBox->layout()->addWidget(button);
				QObject::connect(button, &QRadioButton::toggled, [&widgets](bool checked)
				{
					widgets[0]->getQtWidget()->show();
					widgets[1]->getQtWidget()->hide();
					widgets[2]->getQtWidget()->hide();
					widgets[3]->getQtWidget()->hide();
				});
				button->setChecked(true);
			}
			{
				auto button = new QRadioButton("二分屏");
				groupBox->layout()->addWidget(button);
				QObject::connect(button, &QRadioButton::toggled, [&widgets](bool checked)
				{
					widgets[0]->getQtWidget()->show();
					widgets[1]->getQtWidget()->show();
					widgets[2]->getQtWidget()->hide();
					widgets[3]->getQtWidget()->hide();
				});
			}
			{
				auto button = new QRadioButton("四分屏");
				groupBox->layout()->addWidget(button);
				QObject::connect(button, &QRadioButton::toggled, [&widgets](bool checked)
				{
					widgets[0]->getQtWidget()->show();
					widgets[1]->getQtWidget()->show();
					widgets[2]->getQtWidget()->show();
					widgets[3]->getQtWidget()->show();
				});
			}
		}

		//分屏同步
		{
			auto checkBox = new QCheckBox("分屏同步");
			frameLayout->addWidget(checkBox);
			QObject::connect(checkBox, &QCheckBox::stateChanged, [&viewers, &viewerSync](int state)
			{
				if (state)
				{
					for (auto v : viewers)
					{
						viewerSync->addViewer(v);
					}
					viewerSync->setMasterViewer(viewers[0]);
				}
				else
				{
					for (auto v : viewers)
					{
						viewerSync->removeViewer(v);
					}
				}
			});
		}

		//图层
		{
			auto addLayerCheckBox = [frameLayout](const QString& label, std::function<std::string(std::shared_ptr<odv::Viewer> viewer)> addLayerFunc)
			{
				auto checkBox = new QCheckBox(label);
				frameLayout->addWidget(checkBox);

				QObject::connect(checkBox, &QCheckBox::stateChanged, [checkBox, addLayerFunc](int state)
				{
					if (!g_currentViewer) return;
					auto& viewerData = g_viewerData[g_currentViewer];
					if (auto iter = viewerData.widgetLayerIdMap.find(checkBox); iter != viewerData.widgetLayerIdMap.end())
					{
						g_currentViewer->setLayerVisible(iter->second, (bool)state);
					}
					else
					{
						if (state)
						{
							auto id = addLayerFunc(g_currentViewer);
							if (id.empty()) return;
							viewerData.widgetLayerIdMap.emplace(checkBox, id);
						}
					}
				});
			};

			addLayerCheckBox("影像图", addImage);
			addLayerCheckBox("矢量图", addFeature);
			addLayerCheckBox("颜色填充图", addColorMap);
			addLayerCheckBox("等值线", addContourLine);
			addLayerCheckBox("矢量箭头图", addPointIcon);
		}
	}

	return app.exec();
}

std::string addColorMap(std::shared_ptr<odv::Viewer> viewer)
{
	viewer->setUseOsgModelViewAndProjectionUniforms(true);
	auto options = std::make_shared<odv::ColorMapOptions>();
	options->urls = { "E:/data/odvlib/daily_hats_2021_106.nc" };
	//options->urls = { "E:/HLHT/data/IAP/20210628/daily_hats_2021_106.nc" };
	//options->urls = { "E:/data/odvlib/daily_hats_2021_106.nc" };
	options->varNames = { "t" };
	options->dimOrder = odv::DataDimensionOrder::DepthLatLong;
	options->depthRange = odv::LayerOptions::DataRange(5, 0);
	options->latRange = odv::LayerOptions::DataRange(-90, 180);
	options->longRange = odv::LayerOptions::DataRange(0, 360);

	options->latRange = odv::LayerOptions::DataRange(10, 30);
	options->longRange = odv::LayerOptions::DataRange(110, 20);

	options->colorBarOptions = odv::ColorBarOptionsFactory::instance()->createColorBarOptions(odv::ColorBarPreset::Jet, 0, 30);
	options->colorBarOptions->showTicks = true;
	options->colorBarOptions->showColorBar = true;
	options->colorBarOptions->samples = 5;

	options->clipDisplayedRangeMinValue = -20;
	options->clipDisplayedRangeMaxValue = 50;

	auto id = viewer->addLayer(options);
	if (id)
	{
		ODVLOG_WARNING("Added ColorMap {", *id, "}");
		return *id;
	}

	return std::string();
}

std::string addImage(std::shared_ptr<odv::Viewer> viewer)
{
	auto options = std::make_shared<odv::ImageOptions>();
	options->driver = odv::ImageOptions::Driver::OSG;
	options->urls = { "E:/HLHT/data/EarthElevation/world3.png" };
	//options->urls = { "E:/data/odvlib/world3.png" };
	options->imageLonOrigin = -180;
	options->imageLonSpan = 360;
	options->imageLatOrigin = -90;
	options->imageLatSpan = 180;

	auto id = viewer->addLayer(options);
	if (id)
	{
		ODVLOG_WARNING("Added Image {", *id, "}");
		return *id;
	}

	return std::string();
}

std::string addFeature(std::shared_ptr<odv::Viewer> viewer)
{
	auto options = std::make_shared<odv::FeatureOptions>();
	options->urls = { "f:/test/shp/world.shp" };
	//options->urls = { R"(D:\data\NaturalEarth\10m_cultural\10m_cultural\ne_10m_admin_2_counties.shp)" };
	options->lineWidth = 1.0f;
	options->lineColor = { 0.74, 0.75, 0.78, 1.0 };
	options->fillColor = { 0.94, 0.95, 0.98, 1.0 };

	auto id = viewer->addLayer(options);
	if (id)
	{
		ODVLOG_WARNING(odv::StringUtils::format("Add Feature {{{}}}", *id));
		return *id;
	}

	return {};
}

std::string addContourLine(std::shared_ptr<odv::Viewer> viewer)
{
	viewer->setUseOsgModelViewAndProjectionUniforms(true);
	auto options = std::make_shared<odv::ContourLineOptions>();
	options->urls = { "E:/HLHT/data/IAP/20210628/daily_hats_2021_106.nc" };
	//options->urls = { "E:/data/odvlib/daily_hats_2021_106.nc" };
	options->varNames = { "t" };
	options->dimOrder = odv::DataDimensionOrder::DepthLatLong;
	options->depthRange = odv::LayerOptions::DataRange(5, 0);
	options->latRange = odv::LayerOptions::DataRange(-90, 180);
	options->longRange = odv::LayerOptions::DataRange(0, 360);

	options->colorBarOptions = odv::ColorBarOptionsFactory::instance()->createColorBarOptions(odv::ColorBarPreset::Rainbow, 20, 30);

	options->clipDisplayedRangeMinValue = -20;
	options->clipDisplayedRangeMaxValue = 50;

	options->contourInterval = 1;
	options->lineWidth = 2;
	options->showContourLabel = true;
	options->labelCharacterSize = 150000;
	options->labelFont = "c:/Window/Fonts/FZSTK.TTF";
	options->cullSmallLines = true;
	options->smallLinePointCountThreshold = 100;

	auto id = viewer->addLayer(options);
	if (id)
	{
		ODVLOG_WARNING("Added Contourline {", *id, "}");
		return *id;
	}

	return std::string();
}

std::string addPointIcon(std::shared_ptr<odv::Viewer> viewer)
{
	viewer->setUseOsgModelViewAndProjectionUniforms(true);
	auto options = std::make_shared<odv::PointIconOptions>();
	options->urls =
	{
		"E:/HLHT/data/IAP/20210628/daily_uv_2021_106.nc" ,
		"E:/HLHT/data/IAP/20210628/daily_uv_2021_106.nc" ,
	};
	options->varNames = { "u", "v" };
	options->dimOrder = odv::DataDimensionOrder::DepthLatLong;
	options->depthRange = odv::LayerOptions::DataRange(5, 0);
	options->latRange = odv::LayerOptions::DataRange(0, 50);
	options->longRange = odv::LayerOptions::DataRange(110, 50);
	options->colorBarOptions = odv::ColorBarOptionsFactory::instance()->createColorBarOptions(odv::ColorBarPreset::YlGnBu, 0, 1);
	options->clipDisplayedRangeMinValue = -20;
	options->clipDisplayedRangeMaxValue = 50;

	options->type = odv::PointIconOptions::PointIconType::Arrow;
	options->arrowBaseLength = 100000;
	options->arrowLineWidth = 2.0f;
	options->samplingStep = 5;
	options->enableColorBar = true;
	options->baseColor = { 1, 1, 1, 1 };

	auto id = viewer->addLayer(options);
	if (id)
	{
		ODVLOG_WARNING("Added PointIcon {", *id, "}");
		return *id;
	}

	return std::string();
}